package org.example.mall.admin.service.manage.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.mall.admin.configuration.auth.CustomUser;
import org.example.mall.admin.configuration.auth.check.CheckAuthHandlerChain;
import org.example.mall.admin.configuration.auth.code.ScenesConst;
import org.example.mall.admin.configuration.exception.BaseException;
import org.example.mall.admin.constant.auth.AuthTypeConst;
import org.example.mall.admin.mapper.manage.AccountMapper;
import org.example.mall.admin.model.dto.manage.AccountDto;
import org.example.mall.admin.model.entity.manage.Account;
import org.example.mall.admin.model.entity.manage.AccountAuth;
import org.example.mall.admin.model.entity.manage.Role;
import org.example.mall.admin.model.enums.manage.SysRelationEnum;
import org.example.mall.admin.model.vo.manage.AccountVo;
import org.example.mall.admin.service.manage.*;
import org.example.mall.admin.util.BusOp;
import org.example.mall.admin.util.SecurityUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息 服务实现类
 * </p>
 *
 * @author hzq
 * @since 2021-12-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    private final RoleService roleService;
    private final SysFileService sysFileService;
    private final AccountAuthService accountAuthService;
    private final SysRelationService sysRelationService;
    private final CheckAuthHandlerChain checkAuthHandlerChain;

    @Override
    public Map<String, Object> info() {
        // 认证信息
        CustomUser customUser = SecurityUtil.getCustomUser();
        BusOp.mustNotNull(customUser, "暂未认证");
        // 用户信息
        FutureTask<Map<String, Object>> accountMapTask = new FutureTask<>(() -> baseInfo());
        // 角色列表
        FutureTask<Set<String>> roleCodesTask = new FutureTask<>(() -> {
            List<Role> roles = roleService.accountRoles(customUser.getAuth().getAccountId(), SecurityUtil.getClientId());
            return roles.stream().filter(r -> StrUtil.isNotBlank(r.getRoleCode()))
                    .map(Role::getRoleCode).collect(Collectors.toSet());
        });
        accountMapTask.run();
        roleCodesTask.run();
        Map<String, Object> resultMap = new HashMap<>();
        // 权限列表
        resultMap.put("permissions", AuthorityUtils.authorityListToSet(customUser.getAuthorities()));
        try {
            resultMap.put("auth", accountMapTask.get());
            resultMap.put("roles", roleCodesTask.get());
        } catch (InterruptedException | ExecutionException ignored) {
        }
        return resultMap;
    }

    @Override
    public Map<String, Object> baseInfo() {
        AccountAuth auth = SecurityUtil.getAuth();
        Account account = getById(auth.getAccountId());
        Map<String, Object> authMap = Convert.toMap(String.class, Object.class, auth);
        Map<String, Object> accountMap = Convert.toMap(String.class, Object.class, account);
        accountMap.putAll(authMap);
        accountMap.remove("credentials");
        return accountMap;
    }

    @Override
    public void setDeptIsNull(Integer deptId) {
        getBaseMapper().setDeptIsNull(deptId);
    }

    @Override
    public IPage<AccountVo> getPage(Page page, Account account) {
        return getBaseMapper().getPage(page, account);
    }

    @Override
    public Account getByPhone(String phone) {
        Account condition = new Account();
        condition.setPhone(phone);
        return getOne(new QueryWrapper<>(condition));
    }

    @Override
    @Transactional
    public Boolean add(AccountDto dto) {
        Account exist = getByPhone(dto.getPhone());
        if (exist != null) {
            throw new BaseException("当前手机号已被其他用户使用");
        }
        Account account = new Account();
        BeanUtils.copyProperties(dto, account);
        account.setUnionId(UUID.fastUUID().toString());
        boolean save = save(account);
        sysRelationService.addRelation(account.getAccountId(), dto.getRoles(), SysRelationEnum.ACCOUNT_ROLE.getTbName());
        // 创建手机认证
        editPhone(account.getAccountId(), account.getPhone());
        // 创建用户名认证 默认用户名密码都是手机号
        setUsernamePassword(account, account.getPhone(), account.getPhone());
        // 引用文件
        sysFileService.asyncLinkFile(dto);
        return save;
    }

    @Override
    public Boolean edit(AccountDto dto) {
        Account old = getById(dto.getAccountId());
        // 不允许修改属性 如需修改请单独修改
        dto.setPhone(null);
        Account account = new Account();
        BeanUtils.copyProperties(dto, account);
        boolean update = updateById(account);
        sysRelationService.editRelation(account.getAccountId(), dto.getRoles(), SysRelationEnum.ACCOUNT_ROLE.getTbName());
        // 引用文件
        sysFileService.asyncEditLinkFile(old, dto);
        return update;
    }

    @Override
    public Boolean selfEdit(Account account) {
        Account dto = new Account();
        dto.setAccountId(SecurityUtil.getAuth().getAccountId());
        dto.setAvatar(account.getAvatar());
        dto.setNickname(account.getNickname());
        Account old = getById(dto.getAccountId());
        // 引用文件
        sysFileService.asyncEditLinkFile(old, dto);
        return updateById(dto);
    }

    @Override
    @Transactional
    public Boolean del(Integer accountId) {
        Account old = getById(accountId);
        boolean remove = removeById(accountId);
        // 删除所有角色关联
        sysRelationService.delRelationFromId(accountId, SysRelationEnum.ACCOUNT_ROLE.getTbName());
        // 删除所有认证信息关联
        accountAuthService.delRelation(accountId);
        // 引用文件
        sysFileService.asyncUnLinkFile(old);
        return remove;
    }

    @Override
    @Transactional
    public Boolean bindEmail(String email, String code) {
        // 校验验证码
        checkAuthHandlerChain.check(ScenesConst.BIND, email, code, AuthTypeConst.EMAIL);
        return editEmail(SecurityUtil.getAuth().getAccountId(), email);
    }

    @Override
    public Boolean editEmail(Integer accountId, String email) {
        // 置空用户与该邮箱的绑定
        getBaseMapper().setEmailIsNull(email);
        Account account = getById(accountId);
        BusOp.mustNotNull(account, "用户信息为空");
        account.setEmail(email);
        BusOp.mustSucceed(updateById(account), "绑定邮箱失败");
        return accountAuthService.editAccountEmail(account);
    }

    @Override
    public Boolean bindPhone(String phone, String code) {
        // 校验验证码
        checkAuthHandlerChain.check(ScenesConst.BIND, phone, code, AuthTypeConst.PHONE);
        return editPhone(SecurityUtil.getAuth().getAccountId(), phone);
    }

    @Override
    public Boolean editPhone(Integer accountId, String phone) {
        // 置空用户与该手机号的绑定
        getBaseMapper().setPhoneIsNull(phone);
        Account account = getById(accountId);
        BusOp.mustNotNull(account, "用户信息为空");
        account.setPhone(phone);
        BusOp.mustSucceed(updateById(account), "绑定手机号失败");
        return accountAuthService.editAccountPhone(account);
    }

    @Override
    public Boolean editUsernamePassword(Integer accountId, String username, String password) {
        Account account = getById(accountId);
        BusOp.mustNotNull(account, "用户信息为空");
        return setUsernamePassword(account, username, SecurityUtil.AES.decryptStr(password));
    }

    @Override
    public Boolean setUsernamePassword(Account account, String username, String password) {
        // 判断该账号是否被使用
        AccountAuth auth = accountAuthService.getByCondition(AuthTypeConst.USERNAME, username);
        if (auth != null && !auth.getAccountId().equals(account.getAccountId())) {
            throw new BaseException(String.format("用户名【%s】已被其他用户使用", username));
        }
        // 新建账号密码认证
        if (auth == null) {
            auth = new AccountAuth();
            auth.setAuthType(AuthTypeConst.USERNAME);
            auth.setPrincipal(username);
            auth.setCredentials(SecurityUtil.PASSWORD_ENCODER.encode(password));
            accountAuthService.delRelation(account.getAccountId(), auth.getAuthType());
            return accountAuthService.addRelation(account, auth);
        }
        // 修改密码
        else {
            auth.setCredentials(SecurityUtil.PASSWORD_ENCODER.encode(password));
            return accountAuthService.updateById(auth);
        }
    }

    @Override
    public Boolean editPassword(Integer accountId, String originPassword, String newPassword) {
        LambdaQueryWrapper<AccountAuth> wrapper = Wrappers.<AccountAuth>lambdaQuery()
                .eq(AccountAuth::getAccountId, accountId)
                .eq(AccountAuth::getAuthType, AuthTypeConst.USERNAME);
        AccountAuth auth = accountAuthService.getOne(wrapper);
        if (auth == null) {
            throw new BaseException(String.format("用户【%s】暂未设置账号类型认证", accountId));
        }
        // 解密等到原始密码 前端传入的密码经过前端加密的
        if (!SecurityUtil.PASSWORD_ENCODER.matches(SecurityUtil.AES.decryptStr(originPassword), auth.getCredentials())) {
            throw new BaseException("原密码有误");
        }
        auth.setCredentials(SecurityUtil.PASSWORD_ENCODER.encode(SecurityUtil.AES.decryptStr(newPassword)));
        return accountAuthService.updateById(auth);
    }
}
